#!C:\Perl\bin\perl.exe
#
# acf_streaming.pl
# Version 0.01
#
# Developed by Gerrit Schimpf <gerrit@schimpf.es>
# All rights reserved.
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
#
# Changelog:
# 2010-04-27 - created
# 2010-04-29 - finished version 0.01
#

use strict;
use XML::Simple;
use LWP::UserAgent; 
use Digest::HMAC_SHA1;
use MIME::Base64 qw(encode_base64);
use Data::Dumper;
use Getopt::Long;

my $debug=0;

# key for authorization
my $aws_secret_key = 0;
my $aws_id = 0;

# check for aws keys
$aws_secret_key = $ENV{AWS_SECRET_ACCESS_KEY};
$aws_id = $ENV{AWS_ACCESS_KEY_ID};
if (!$aws_secret_key || !$aws_id) {
	print "Environment not set. \nSet AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.\n\n";
	exit(1);
}

# get the acutal time from s3 api, will be used for authorization.
sub getServerDate() {
	my $ua = LWP::UserAgent->new;

	my $req = HTTP::Request->new(GET => 'https://cloudfront.amazonaws.com/date');
	my $res = $ua->request($req);

	if ($res->is_success) {
		die "Couldnt get date from server";
	}
	else {
		return $res->header("Date");
	}
}

# Created the signature for auhtorization
sub createSignature {
	my ($key,$string) = @_;

	my $hmac = Digest::HMAC_SHA1->new($key);
	$hmac->add($string);
	my $signature = encode_base64($hmac->digest, "");
	return $signature;
}

# output an errormessage from the API
sub error {
	my ($data) = @_;
	die $data->{Error}->{Code} ."\n". $data->{Error}->{Message} ."\n";
}

# output result from api request. At the moment just a dump.
sub printResult {
	my ($data) = @_;
	print Dumper($data);
}

sub handleResponse {
	my ($res) = @_;

	# Check the outcome of the response
	if ($res->is_success) {
		print $res->content. "\n" if ($debug);
		my $data = XMLin($res->content);
		if ($res->header("ETag")) {
			$data->{ETag} =$res->header("ETag");
		}
		return $data;
	}
	else {
		my $data = XMLin($res->content);
		error($data);
	}
}

sub makeRequestPost {
	my ($data, $id, $sig, $date, $root, $func) = @_;

	my $xsimple = XML::Simple->new();
	my $content = $xsimple->XMLout($data,
		noattr => 1,
		RootName => $root,
		xmldecl => '<?xml version="1.0" encoding="UTF-8">');

	print "Request:\n$content\n" if ($debug);

	my $ua = LWP::UserAgent->new;
	my $req = HTTP::Request->new(POST => "https://cloudfront.amazonaws.com/2010-03-01/$func");
	$req->header("Authorization", "AWS $id:$sig");
	$req->header("Date", "$date");
	$req->content($content);

	my $res = $ua->request($req);
	return handleResponse($res);
}

sub makeRequestPut {
	my ($data, $id, $sig, $date, $root, $func, $ifm) = @_;

	my $xsimple = XML::Simple->new();
	my $content = $xsimple->XMLout($data,
		noattr => 1,
		RootName => $root,
		xmldecl => '<?xml version="1.0" encoding="UTF-8">');

	print "Request:\n$content\n" if ($debug);

	#make the request
	my $ua = LWP::UserAgent->new;
	my $req = HTTP::Request->new(PUT => "https://cloudfront.amazonaws.com/2010-03-01/$func");
	$req->header("Authorization", "AWS $id:$sig");
	$req->header("Date", "$date");
	$req->header("If-Match", "$ifm");
	$req->content($content);

	my $res = $ua->request($req);
	return handleResponse($res);
}

sub makeRequestGet {
	my ($id, $sig, $date, $func) = @_;

	my $ua = LWP::UserAgent->new;
	my $req = HTTP::Request->new(GET => "https://cloudfront.amazonaws.com/2010-03-01/$func");
	$req->header("Authorization", "AWS $id:$sig");
	$req->header("Date", "$date");

	my $res = $ua->request($req);
	return handleResponse($res);
}

my $sdate = getServerDate();
my $signature = createSignature($aws_secret_key,$sdate);

sub postStreamingDistributionConfig {
	my ($data) = @_;
	my $streamingDistribution = makeRequestPost($data, $aws_id, $signature, $sdate, "StreamingDistributionConfig", "streaming-distribution");
	print Dumper($streamingDistribution);

}

sub getStreamingDistributionList {
	my $data = makeRequestGet($aws_id, $signature, $sdate, "streaming-distribution");
	print Dumper($data->{StreamingDistributionSummary}) if ($debug) ;

	my $distris = $data->{StreamingDistributionSummary};

	if (ref($distris) =~ /HASH/) {
		$distris = [$distris];
	}
	print "\nId, Enabled, Origin, LastModifiedTime, Status, Comment, DomainName\n";
	for my $distri (@$distris) {
		print $distri->{"Id"}. ", ";
		print $distri->{"Enabled"}. ", ";
		print $distri->{"Origin"}. ", ";
		print $distri->{"LastModifiedTime"}. ", ";
		print $distri->{"Status"}. ", ";
		print $distri->{"Comment"}. ", ";
		print $distri->{"DomainName"}. "\n";
	}
	print "\n";
}

sub getStreamingDistribution {
	my ($id) = @_;
	my $data = makeRequestGet($aws_id, $signature, $sdate, "streaming-distribution/$id");
	print "\nDistribution for [$id]\n\n";
	printResult($data);
	return $data;
}

sub getStreamingDistributionConfig {
	my ($id) = @_;
	my $data = makeRequestGet($aws_id, $signature, $sdate, "streaming-distribution/$id/config");
	print "\nDistribution configuration for [$id]\n\n";
	printResult($data);
	return $data;
}

sub putStreamingDistributionConfig {
	my ($data, $id) = @_;
	my $ifm = $data->{ETag};
	makeRequestPut($data, $aws_id, $signature, $sdate, "StreamingDistributionConfig", "streaming-distribution/$id/config", $ifm);
}

sub help {
    print STDERR <<EOF;

usage:
acf_streaming.pl --help                                 print this help file
acf_streaming.pl --list                                 list all streaming distributions
acf_streaming.pl --create -o ORIGIN -c COMMENT          create a new streaming distribution 
acf_streaming.pl --get_info --id DISTRIBUTION_ID         get info about a streaming distribution 
acf_streaming.pl --get_config --id DISTRIBUTION_ID       get the configuration from a streaming distribution 
acf_streaming.pl --update --id DISTRIBUTION_ID [--trusted self|AWS_ACC_NO]   change the trusted signers

EOF
}

sub convertTrusted {
	my ($trusted)  = @_;
	my @userH;
	my @trustedUser = split(/,/,join(',',@$trusted));
	foreach my $u (@trustedUser) {
		print "User $u\n";
		if ($u =~ /self/) {
			push (@userH, {"Self" => ""});
		} else {
			push (@userH, {"AwsAccountNumber" => $u});
		}
	}
	return \@userH;
}

sub checkParam {
	my ($name, $value) = @_;

	if (!$value) {
		print "ERROR: parameter --$name missing\n";
		exit(1);
	}
}

sub handleParams {
	my $verbose = 0; 
	my $help = 0;

	#cmds
	my $list = 0;
	my $create = 0;
	my $get_info = 0;
	my $get_config = 0;

	# options
	my $id = 0;
	my $update = 0;
	my $comment = 0;
	my @trusted ;
	my $origin=0;
	my $oai=0;

	my %h = ('verbose' => \$verbose, 'debug' => \$debug, 'help' => \$help, 'list' => \$list, 'get_info' => \$get_info,
			 'get_config' => \$get_config, 'id' => \$id, 'update' => \$update, 'comment' => \$comment, 'trusted' => \@trusted,
	   		 'create' => \$create, 'origin' => \$origin, 'origin-access-identity' => \$oai );
	GetOptions (\%h, 'verbose', 'debug', 'help', 'list', 'create', 'get_info', 'get_config', 'update', 
				'id=s', 'comment=s', 'trusted=s', 'origin=s', 'origin-access-identity=s');
	if ( $help ) {
		help();
		exit();
	}
	if ($create) {
		checkParam("origin", $origin);
		checkParam("comment", $comment);
		checkParam("origin-access-identity", $oai);

		my %data = (
			Origin => $origin,
			CallerReference => time(),
			Comment => $comment,
			Enabled => "true",
			OriginAccessIdentity => "origin-access-identity/cloudfront/$oai",
		);
		postStreamingDistributionConfig(\%data);
		exit(0);
	}
	if ($list) {
		getStreamingDistributionList();
		exit();
	}
	if ($get_info) {
		if (!$id) {
			print "Parameter '--id DISTRIBUTION_ID' missing.\n";
			exit();
		}
		getStreamingDistribution($id);
		exit();
	}
	if ($get_config) {
		if (!$id) {
			print "Parameter '--id DISTRIBUTION_ID' missing.\n";
			exit();
		}
		getStreamingDistributionConfig($id);
		exit();
	}
	if ($update) {
		if (!$id) {
			print "Parameter '--id DISTRIBUTION_ID' missing.\n";
			exit();
		}
		# get the SDC
		my $sdc = getStreamingDistributionConfig($id);
		printResult($sdc);

		# change comment if set
		if ($comment) {
			$sdc->{Comment} = $comment;
		}

		# change trusted if set
		if (@trusted) {
			addTrusted($sdc,\@trusted);
			my @userH = convertTrusted(\@trusted);
			$sdc->{TrustedSigners}= \@userH;
		}

		# output the request if debug
		my $xsimple = XML::Simple->new();
		my $content = $xsimple->XMLout($sdc,
			noattr => 1,
			RootName => "opt",
			xmldecl => '<?xml version="1.0" encoding="UTF-8">');
		print $content ."\n\n" if ($debug);
		
		putStreamingDistributionConfig($sdc, $id);
		print "Distribution config changed\n";

		exit();
	}
	help();
}

handleParams();

__END__

=head1 NAME

acf_s Amazon cloudfront configuration helper - streaming

=head1 DESCRIPTION

A little tool to use the amazon cloudfront api for streaming distributions.

This cript is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 


 acf_streaming.pl --help

=head1 USAGE

The aws key_id and the secret key needs to be set in the two environment varliables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.

 export AWS_ACCESS_KEY_ID=YOUR_AWS_KEY_ID
 export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY

 acf_streaming.pl --CMD [OPTIONS] [--debug]

Use the option --debug to display the xml request made by acf, and the full result from the API.


=head2 List all streaming distribution

Get a list of all streaming distributions and print it as CSV to stdout. 

 acf_streaming.pl --list [--debug]

=head2 Create a new streaming distribution

Create a new streaming distribution using the given bucket.

acf_streaming.pl --create --origin B<ORIGIN> --comment B<COMMENT> --origin-access-identity B<OAI> [--trusted self|AWS_ACC_NO]

B<ORIGION> The s3 bucket used for distribution

B<COMMENT> A comment which will be added to the distribution configuration.

B<OAI> Origin access identity. Identity used by cloudfront to access the bucket. Don't include the "origin-access-identity/cloudfront/" part.

B<TRUSTED> A list of trusted users. These users can create signed URLs for this streaming distribution.

I<EXAMPLE>:

acf_streaming.pl --create --origin mybucket.s3.amazonaws.com --comment "Streaming my bucket" --origin --origin-access-identity E4DNRLKSU2U7U --trusted self

This command will create a streaming distribution on the bucket mybucket. Only self (User with the key defined in the
environment AWS_ACCESS_KEY_ID) will be able to sign URLs for this distribution. The identity E4DNRLKSU2U7U will be used to access
the S3 bucket.


=head2 Get information about a streaming distribution

 acf_streaming.pl --get_info --id DISTRIBUTION_ID
get info about a streaming distribution 

=head2 Get a streaming distribution configiugation

 acf_streaming.pl --get_config --id DISTRIBUTION_ID
get the configuration from a streaming distribution 

=head2 Change the configuration from a streaming distribution

 acf_streaming.pl --update --id DISTRIBUTION_ID [--trusted self|AWS_ACC_NO]
change the trusted signers

=head1 COPYRIGHT

Copyright 2010 Gerrit Schimpf

Permission is granted to copy, distribute and/or modify this 
document under the terms of the GNU Free Documentation 
License, Version 1.2 or any later version published by the 
Free Software Foundation; with no Invariant Sections, with 
no Front-Cover Texts, and with no Back-Cover Texts.

=cut
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            